Fork me on GitHub
#beginners
<
2017-09-15
>
donyorm04:09:27

searching on the internet doesn't reveal the clojure.java.api.Clojure class. I though aot was the best way to create interop

donyorm04:09:45

*java -> clojure interop

donyorm04:09:14

So I'm making a program in clojure that creates a java-accesible api. Should my api class be written in java that uses clojure.java.api.Clojure to call the clojure namespaces?

donyorm04:09:59

I don't want my users to have to be able to simply important and call a class

noisesmith04:09:51

You can use aot of course - but there are complications (as we were discussing) so it's not always the most convenient option. You can also make a small java class that uses clojure.java.api.Clojure internally and exposes a more normal java api to your callers, this is useful for example if you also want your code to be usable as a clojure lib

noisesmith04:09:09

because using a library that aot-compiles itself from clojure is a pain

donyorm04:09:12

yeah I've been noticing that. I planned to use require inside functions so that only the "main" namespace and the java api (not clojure api) namespace use aot. Does that seem reasonable?

noisesmith04:09:30

yes, this is what I do with my primary app

noisesmith04:09:02

but sometimes just using clojure.java.api.Clojure is simpler (I'm considering switching to that in my app but haven't needed to)

donyorm04:09:17

it would certainly make it easier to write javadocs

noisesmith04:09:26

right, or if you need annotations...

donyorm04:09:10

That macro is was working on with you in #clojure is for the aot api class, so yeah a little more complexity. I think I'll try the java class and see how it goes. It seems to be the preferred way on http://clojure.org

noisesmith04:09:24

oh man - it's so rough I just noticed a dumb typo facepalm

donyorm04:09:41

that link isn't working either 😕

noisesmith04:09:01

oh is it still private - one moment I can change that

noisesmith04:09:29

should be fixed now - it's very raw but should be a decent example already

donyorm04:09:42

What's the scope for require in the context of a java class. Will a static block like this one allow all (static) functions in the class to use the require namespace?

static {
        IFn require = Clojure.var("clojure.core", "require");
        require.invoke(Clojure.read("oss-world.api"))
    }

noisesmith04:09:27

and yeah that code actually runs

noisesmith04:09:20

oh, I should have put that require in a static block, now that I do a little research

noisesmith04:09:39

@donyorm I am finding all sorts of problems - this code is not as tested as I thought but I should have working code pushed soon

donyorm04:09:12

I'm really just wrapping function calls to the clojure api to my program, not doing anything very complex so I should be fine

noisesmith04:09:07

OK - the code on git actually compiles and runs now

noisesmith04:09:59

the code still doesn't make sense yet, but is no longer blatantly erroneous

donyorm04:09:29

cool, thanks

OctarineSorcerer14:09:20

Hey all! Is it okay if I ask about why Clojure would be good for implementing a card game? I'm very new to the whole idea of Clojure, and it seems it can really shorten things (The http://jinteki.net repo has some very short implementations of things I expected would be much longer in nigh any language), but the syntax and all those brackets kind of pain me some - along with (from what I can see) it being pretty slow. Apologies if this should be in #clojure-gamedev, but I haven't got a reply there after a few hours

dpsutton14:09:09

I can't imagine a card game being played fast enough that speed of language would come into play

dpsutton14:09:51

and that's accepting, for arguments sake, that clojure is slow, which i disagree with

donaldball14:09:02

That’s a fairly broad question, but I’ll try to take a stab at it. Clojure could be good for implementing a card game if you wrote it in a functional style, in that you would be passing the game state around explicitly, and could verify that your functional operations on it implemented the game invariants correctly.

OctarineSorcerer14:09:27

Thank you! The speed point is quite a viable one Yes, functional is the style I was thinking (as I'd quite like to get more familiar with it), and am currently debating using either F# or Clojure

donaldball14:09:51

As a new clojure programmer, the syntax may be challenging to become adjusted to, but most folk find that within a week or two and with the use of an editor that provides support for managing paren structures (e.g. with paredit and friends) the syntax becomes a desirable feature, not a hindrance.

dpsutton14:09:57

> The speed point is quite a viable one I don't see how

OctarineSorcerer14:09:19

@dpsutton Ah sorry, I meant in terms of it not mattering either way in a card game

dpsutton14:09:27

yeah i agree.

dpsutton14:09:00

as to the syntax, structural editing makes lisps much nicer to work with, provided you have an editor that indents and formats correctly. the parens make it easy to work with and make it easier to digest what's going on. perhaps counter-intuitive at first but i would really miss it if i went back to a {} scoped language

OctarineSorcerer14:09:33

Structural editing? As in representing scope, etc with indentation more than anything else?

donaldball14:09:27

More in terms of automatically balancing parens, not letting you unbalance parens (easily), allowing you to manipulate the content and position of sexps easily

dpsutton14:09:55

so in a C type language, an if statement looks like

if (condition) {
   some random statements; 
} else {
  some more statements;
}
and to move or edit things you have to grab all the lines and there's no structural way to grab or delimit the entire if statement. but lisps ensure that the form (if whatever else may be in here) has a balancing paren on the other side. editors are aware from the source code, rather than compiler output, the extent of this expression and can easy add more things to it, delete the whole thing, copy it for moving, whatever

dpsutton14:09:10

and within a form, its easy to have a notion of "delete the rest of this if statement" which cannot exist in other languages without your editors delete asking the AST the extent of the rest of the if statement

OctarineSorcerer14:09:29

That does seem like it'd ease the parenthepain quite a lot, especially while starting. Oh okay! So editors find it very easy to manipulate individual (and groups of) statements, instead of being down to the level of characters? That's actually pretty awesome

dpsutton14:09:30

right. in emacs, when i say "delete the rest of this line", it won't haphazardly erase characters, it will kill until the end of a logical expression. the parens remain balanced which mean i have deleted a semantic object rather than characters. it's trivial to know the extent of expressions since an expression has the form identifier | (list of identifiers). that's the shape of everything in the language, except that sometimes for clarity clojure uses [] or {} for delimiters

OctarineSorcerer14:09:10

Ah okay! That sounds like something it'd be hard to come back from once you've started. Is the difference in ease-of-use less stark when compared to a language that depends mostly on indentation, eg Python/F#? As opposed to curly craces

eggsyntax23:09:16

It's VERY hard to come back from 😆

dpsutton14:09:41

play around and find out 🙂 write your game in both f# and clojure and compare for your own sake. ML style languages have a lot to like as well. find out where you are. there's no right choice in language. find out what gels for you and then build use only that language for a year

OctarineSorcerer14:09:54

That's a really good way of trying it I think. Porting ought to be much easier than writing from scratch, and having experience in more things can only be good

dpsutton14:09:16

i learned a lot of languages for a while but its always superficial. gotta spend some time with a language, it doesn't matter which one. but get used to how one language works, its idioms, its strengths. do that for a year. learning syntax and simple operations in a bunch of languages doesn't lead to real insight, in my opinion. but i've been wrong before so who knows

donaldball14:09:09

I learned a lot about writing good clojure from writing a small haskell app

OctarineSorcerer14:09:28

I get what you mean - personally, I think that if you can nail down syntax and such for a language that very much shares a lot with one you already know a great deal of (eg C#/Java), but ones that aren't so close, you've gotta stick around until it really clicks @donaldball How so? I haven't used Haskell, but I've been using F# some and from what I hear it takes its roots there

donaldball14:09:55

Forcing myself to consider to describe the population and succession of types was a good exercise

OctarineSorcerer14:09:33

You've lost me there - describing population and succession of types?

donaldball14:09:31

The set of types the program needs, and how the needed functions will transform types into other types

OctarineSorcerer14:09:18

Ah, I think I've heard that called data-driven programming or something like that? I've only seen much about it recently, but it does seem like it'd be really good for another way of thinking about things

jlmr15:09:11

Hi all, macro's keep confusing me. I have the following macro:

(defmacro defschema
       [name conn label & constraints]
       `(do
           (update-schema ~conn ~label ~@constraints)
           (def ~name (output-schema ~label ~@constraints))))
And usage like this:
(defschema something connection "SomeLabel"
       (unique "someproperty")
       (unique "someotherproperty"))
The unique function is supposed to do something with the property and return a simple map, all very straightforward. However it doesn't get called in the macro. Can someone explain to me why this is and what a good solution would be? Thanks in advance!

danm15:09:33

You're destructuring the unique calls with ~@

danm15:09:45

Is that doing the right thing?

danm15:09:00

If you do a macroexpand in the repl, what does it tell you?

danm15:09:27

(macroexpand '(defschema something connection "SomeLabel"
       (unique "someproperty")
       (unique "someotherproperty")))

danm15:09:49

I suspect that it's doing something like passing the 2 unique calls as 2 list arguments to update-schema, rather than calling the unique function and passing each result to update-schema

jlmr15:09:47

@carr0t yes I think you're right. But how to correct it so it calls the unique function?

madstap16:09:31

@jlmr I find that when writing macros it is helpful to write out an example of how you want to call it together with the code you want that call to expand to.

madstap16:09:32

If that macroexpansion isn't what you want, what code do you want your macro to write?

jlmr16:09:00

@madstap that actually looks correct, but it seems the unique function doesn't get called after the expansion

jlmr16:09:52

I guess the unique's in your snippet should be foo.bar/unique

madstap17:09:15

Right, so the backquote qualifies update-schema and output-schema with the namespace where the macro is defined, while the code in constraints is passed as-is. So if you call the macro in another namespace foo.baz, it will look for foo.baz/unique. If you want that to be foo.bar/unique you could just require it from foo.bar: (ns foo.baz (:require [foo.bar :refer [unique]]))

jlmr17:09:08

@madstap but when I call it from foo.bar it doesn't work for some reason

jlmr17:09:17

Unique doesn't get called at all

madstap17:09:49

You're defining the macro and calling it all in the same namespace?

madstap17:09:10

Which also contains the definition of unique?

jlmr17:09:06

For now at least, while I'm testing it

jlmr17:09:49

@madstap the funny thing is that update-schema and output-schema get prefixed with the namespace while unique does not. Might it have something to do with the unsplice-quote?

madstap17:09:41

You are right about the unquote-splice being the reason for unique not being qualified with the foo.bar namespace. But that doesn't explain why unique is not being called at all. It doesn't really make sense to qualify code that the user of the macro passes in.

jlmr17:09:33

Added a println call to unique and it is indeed being called. So the error is happening somewhere else. To some extent a relief: it seems I have written the macro correctly which would be a first 😉.

jlmr17:09:41

Will look into it further now

jlmr18:09:30

Fixed it, lots of sloppy errors elsewhere in the code... 😬 Thanks for your help! 👍

shakdwipeea17:09:17

what would be the idomatic way of error signaling and handling? for example if a function has failed how would it return the value/error reason and how should that be propagated across function chains. In case of nil-puning, I am having trouble sending the error cause, should I look for monadic approaches like Failjure.

noisesmith17:09:33

@shakdwipeea there are various options but sadly since we are on the jvm, we need to handle Exceptions no matter how we design our apps. Which means that any other abstraction is an extra complexity added to the complexity of Exception handling. A good compromise in my experience is to use ex-info to create an exception that has data attached, and use ex-data to extract the attached data if any in a catch clause.

noisesmith17:09:47

this is largely an issue of taste / opinion though, there are definitely other approaches (including various Monad libraries that have Option types or Maybe to represent failure states)

noisesmith17:09:51

another thing to pay attention to is that if you use future (or tools that build on top of it) you won’t see exceptional conditions if you don’t access the return value of that future, which in some cases might mean putting a try/catch inside every future to ensure the errors are logged properly instead

didibus18:09:47

Can't you set the default exception handler for that?

noisesmith18:09:54

no because the future code captures and isolates the exception in order to deliver it to you when you dereference the return value

noisesmith18:09:03

so the exception doesn’t get thrown if it gets as far as the future code

noisesmith18:09:55

(or, more precisely, future captures the exception unconditionally, and rethrows if/when you dereference, in the context where you tried to use the return value, but only if you ever do so)

didibus20:09:52

I was afraid of this. Wouldnt it make sense to capture and rethrow, so that the default handler could optionally be used. Not sure if clojure under the hood uses Java future, if so, it wouldnt be possible to change, but if not, would there be downsides?

noisesmith21:09:01

who would rethrow? when? how can it do this from outside the future?

noisesmith21:09:21

would you run a separate thread with the express job of reading exceptions off a queue and throwing them?

noisesmith21:09:38

and yes, clojure is using java Future to implement futures (and one way around this is to use Thread instead, which would throw to the uncaught error handler, and then add on the binding conveyance for *dynamic-vars*

noisesmith21:09:58

you would also want to use a promise or something like it to replicate the deref behavior I guess

noisesmith21:09:00

but also implement Thread future-cancel would work (via the interrupt method)

noisesmith21:09:46

oh wait, future-cancel uses a method on Future, so you would need to implement Future to do this cleanly

noisesmith21:09:30

it’s in Future’s contract to throw on get (what deref calls in clojure) so you have to choose between throwing the same exception twice (once to the global handler, once to the one accessing the data) or break the expected behavior (I don’t know what the consequences of that would be, probably depends on what third party libs you are using?)

didibus04:09:01

Ya, I was thinking of throwing twice.

didibus04:09:58

The future would capture the exception, but also rethrow it. If you don't put a default handler, that rethrow just disapear in the ether of uncaught thread exception, but when you get on the future, it was captured and gets thrown. But, you can also choose to set a default handler, and handle it when its rethrown, which would be useful for side effecting futures.

shakdwipeea18:09:03

In case of using ex-info and ex-data,what I am looking for is chaining multiple expression ,sth like some-> but also get back the error reason. Also if I want to handle java specific exception then should I wrap that in a ex-info ?

noisesmith18:09:09

wrapping in ex-info and re-throwing is definitely an option, if you aren’t in a position in that code to know how to recover but a caller might want to recover

dimovich18:09:56

hello, what is a good way of adding a repl to your app?

noisesmith18:09:56

fundamentally a nil in Clojure is not an erroneous condition - it’s considered normal

noisesmith18:09:33

@dimovich the simplest thing is to use is the clojure socket repl, which can be started via a simple setting provided to any app running a relatively recent clojure version (1.8.0+) https://clojure.org/reference/repl_and_main#_launching_a_socket_server

noisesmith18:09:51

that doesn’t provide readline or other editor specific integrations like nrepl does though

noisesmith18:09:36

if you need nrepl, you can pull in nrepl as a dependency - it’s a lot bigger, takes longer to start up, and has a lot more features https://github.com/clojure/tools.nrepl#embedding-nrepl-starting-a-server

dimovich18:09:07

@noisesmith thanks for the info!

shakdwipeea18:09:12

@noisesmith using a macro to create sth akin to some-> and return the error or value in a map, this should then also catch any Java Exception. Is going along this line a good way?

noisesmith18:09:07

for certain usages I could see that - one option to consider is to make an instance of reduced with the ex-info object in it if you hit an error, since there’s already a number of things that stop and return something early if it is wrapped in a reduced call

noisesmith18:09:58

for something that’s a functional abstraction, consider not using a macro (for the logic at least…) because macros are for creating syntaxes and don’t operate on the level of runtime values

noisesmith18:09:17

if you also want to make it easier to use via a syntax, write the macro last as a wrapper

shakdwipeea18:09:25

@noisesmith I see this seems like a good approach, I will try to make a function for handling the shortcuting and a macro for threading through multiple s-expressions using the function.

noisesmith18:09:24

@shakdwipeea it’s also worth looking at cats from funcool, which has Maybe and Either built in, and I think Either pretty much encapsulates the semantics you want already - the impression I get is that cats is more usable, more maintained, and has more users than the other clojure monad options http://funcool.github.io/cats/latest/

noisesmith18:09:32

something that operates on Either can return a left which “bottoms out” from all other Either operations, or a right, which is passed in as a value for normal operation etc.

shakdwipeea18:09:30

Looks interesting, will go through this.

noisesmith18:09:51

but just to be clear, you started out asking about “idiomatic” which would be using ex-info / ex-data instead of these other haskell-inspired options, just FYI

shakdwipeea18:09:46

Actually, that was my very doubt. I was able to find multiple error handling attempts using various monadic constructs, so I was wondering if I should look along that way or use a custom function to shorcuit as necessary and return the error as necessary. The main aim in both the cases remaining a sane way to thread multiple s-expression and build a value.

didibus21:09:13

Can't you just thread as normal, and where you get tje returned value, have a try/catch around it?

didibus21:09:36

(try (-> wtv (somefn) (anotherfn) (somemorefns)) (catch Exception e "error occurred"))

didibus21:09:53

So it returns either the successful result or the string "error occured" otherwise. Or wtv you want to return on error instead.

Drew Verlee20:09:07

Does anyone have a good resource on 1) The boring bits of clojure? Maybe something like file structure? e.g why are things in a src/ directory… or docs/ vs having a readme. I recently had to do a small project and realized i really dont understand some of the basics that get taught probably via mentorship. No one is writing hot blog posts on this stuff.

Alex Miller (Clojure team)20:09:45

lein help sample will tell you a lot of things about lein, but specifically if you look in “Filesystem Paths” you will find some good info about typical project structure

Alex Miller (Clojure team)20:09:58

I think this is covered in one or both of the two Prag books I’ve worked on Clojure Applied and Programming Clojure, 3rd ed

Alex Miller (Clojure team)20:09:07

kind of blurred together now with time :)

noisesmith20:09:27

@drewverlee in case it isn’t clear, having a src directory and docs directory are not Clojure design decisions, and you can get by just fine without either, they are things leiningen sets up as defaults

noisesmith20:09:59

what Clojure looks for is a resource or file somewhere on the classpath with a relative path to the classpath root matching the namespace - lein happens to name that classpath entry src in default projects and there’s not much reason to second guess it

noisesmith20:09:34

(though sometimes you’ll see it subdivided if you are using more languages in one project)

noman20:09:33

hey guys, playing with doing stuff in clojure again. anyone got any advice to clean this up? This nested if stuff is pretty gnarly, is this idiomatic clojure style?

(def user {:age 100})
(def user2 {:name "Noman"
            :addresses [{:street "123 Ashland St"
                         :city "Boston"
                         :state "MA"
                         :zip-code 12345}
                        {:street "456 Old St"
                         :city "Oakland"
                         :state "CA"}
                        {:city "Bobtown"
                         :zip-code 12345}]})
(def user3 {:name "Jan"
            :age 44
            :addresses [{:city "Mashville"}
                        {:street "Pratt St"}]})

(defn greet
  "Greets a user"
  [{[{curr-street :street :as curr-addr}
     {prev-street :street
      prev-city :city :as prev-addr}] :addresses
    name :name :or {name "there"}}]
  (str
    "Hi "
    name
    ", I see you "
    (if (nil? curr-addr)
      "don't have a place to live "
      (str
        "live on "
        (or curr-street "an unknown street")
        " "))
    "right now, and you used to "
    (if (nil? prev-addr)
      "not live anywhere before this."
      (str
        "live "
        (and prev-city (str "in " prev-city))
        (if (nil? prev-street)
          "nowhere at all"
          (str (and
                 prev-city
                 ", ")
               "on "
               prev-street))
        "."))))

(println (greet user))
(println (greet user2))
(println (greet user3))
here's a link to it http://replit.net/LIYq/1

noman20:09:05

if my addresses list is a list of current address followed by previous addresses, it should probably be a list, right? not a vector? so it has quick access to the head?

seancorfield21:09:08

(first some-vector) is fast. Vectors are O(1) access to all elements (by index) and some of the functions are optimized to use index access. Lists are only O(1) for the first element (and essentially O(n) for the rest). So using vector is probably what you want here.

seancorfield21:09:29

(I also offered some suggestions in the main channel)

madstap21:09:46

@noman If you expect to add to and read from the front a lot, I'd just reverse it, with the current address last. Then you have quick access to the current one by using peek (for vectors prefer this over last) and quick addition of the new current one by using conj.

seancorfield21:09:01

That's an interesting point. There's an argument on both sides for the order of the addresses in the list/vector: current at the head (most recent to least recent) or in actual date order (least recent to most recent) so the current one is always last.

noman21:09:27

thanks for the advice guys, why is peek better than last?

seancorfield21:09:29

See the docstrings -- it's about performance on vectors.

noman21:09:53

oh yeah, first class doc strings are ❤️

seancorfield21:09:19

BTW, for big pieces of code, use a snippet (via the + to the left of the input box in Slack) or just post a link with the code somewhere else. That's "friendlier" that posting it all inline with triple-backticks.

Drew Verlee20:09:49

@noisesmith good point. Those are specific to lein.

Drew Verlee20:09:40

thanks @alexmiller i’ll take a look at those again.

seancorfield20:09:41

@noman It's more idiomatic to just use the value in conditions rather than explicitly test for nil: (if curr-addr ...) rather than (if (nil? curr-addr) ...) (and swapping the then/else expressions -- or else use (if-not ...)

seancorfield20:09:22

Also, although (and prev-city ...) works, I think it would be clearer to use (when prev-city ...) in both cases.

seancorfield20:09:22

You could break the main greet function up a bit, with helper functions for current address and previous address -- and then you could simplify the destructuring as well, since you'd only need [curr-addr prev-addr] in greet and you could destructure for street/city/state/zip in each helper, without needing to rename parts.

noman21:09:41

@seancorfield ah great suggestions, thank you. i'll take your advice and refactor a bit