Fork me on GitHub
#clojure
<
2017-04-23
>
vitruvia00:04:42

@zak yes after a lot of explanation I understood that. Thanks! I still find it strange that thy would not simply use the second construction in the koans; much easier to understand for a beginner.

qqq00:04:32

is there a way to, via reader macros write things that are ignored by clojure runtime, but are useful to linters ?

cfleming03:04:36

@qqq Depends what you want to be able to annotate. You could use metadata, but not everything accepts it. If you want to be able to mark everything, then something like #_:my-flag can go anywhere in the file.

lincpa04:04:08

@lxsameer html -> hiccup -> markdown

lincpa04:04:21

or try shell/sh pandoc.exe xx.html xx.md

qqq08:04:32

why can't I write:

(case 'a
  'a 20
  'b 30)

rauh08:04:32

@qqq omit the '

rauh08:04:36

You probably know that: 'foo => (quote foo), and (case x (0 1 2) "0 1 or 2") so (case x 'x "x") is (case x (quote x)).

lsenjov08:04:59

Why are quotes considered equal?

lsenjov08:04:25

Well, in the scenario of cases, anyway?

rauh08:04:04

It just checks for the SYMBOL "quote" not the special form quote

Ethan Miller09:04:17

if i have a function that returns a uuid, how do i fill a vector with a specified number of uuids?

Ethan Miller09:04:21

I got it: (mapv (fn [x] (uuid)) (range 10)). what threw me off was that the # macro seems to require that you use % arg.

Ethan Miller09:04:52

I kept getting an arity error when I did: (mapv #(uuid) (range 10)).

rauh09:04:29

@ezmiller77 You probably want repeatedly

dominicm09:04:31

Is there a library which produces a dead-simple AST of clojure code? I don't want it to do any evaluations. The info I want is: 1. form (+ children) 2. position in reader. line via LineReader. column too if possible. I want to do some basic linting. Current system doesn't allow me to load in an arbitrary string for the clojure code, and depends on the eval'd version of the function. No good for realtime feedback.

slipset10:04:35

I think you'll find what you're looking for here in this video https://youtu.be/KhRQmT22SSg

tbaldridge13:04:19

Yep, or tools.analyzer.jvm specifically

tbaldridge13:04:44

Be aware though, it only works on syntactically correct code. Missing parens will break the reader. Some editors support linting of broken code, but such readers are rather hard to write.

dominicm14:04:04

@tbaldridge I'm happy to just bail when that happens.

dominicm14:04:43

@tbaldridge In fact, it'll be very common, as I expect this to be used from editors in an as-you-type situation.

qqq17:04:43

(deftype LVar [name])
(LVar. "hello-world")
now, what do I have to override to make it prettyprint display "hello-world" ?

dominicm17:04:58

@qqq defmethod clojure.core/print-method LVar

qqq17:04:41

@dominicm : thanks! just got it working

qqq19:04:42

is there something that is (or list? vector?) ?

qqq19:04:54

basically it accepts anything that is either a list or a vector, (or more genraly, anything I can map over)

dominicm19:04:26

@qqq sequential? is it?

noisesmith19:04:28

well, there's coll?

noisesmith19:04:35

you can map over non-sequential things

qqq19:04:39

hmm, that includes set and map right?

noisesmith19:04:42

hey, you can even map over a string

qqq19:04:48

actually, I want ordered collections

dominicm19:04:56

seqable? is available in clojure 1.9

souenzzo19:04:11

What's the difference with coll?

dominicm19:04:46

@U2J4FRT2T It works with anything that can be map'd over. I believe the big thing is strings etc.

souenzzo19:04:21

From lumo 1.9.482

cljs.user=> (for [f [coll? seqable?]](mapv f [#{:a :b} {:a :b} [:a :b] '(:a :b) :a]))
([true true true true false] [true true true true false])
What case dat seqable differs from coll?

souenzzo19:04:34

Oh, strings... Right... Anotherday I was looking for #(or (sequential? %) (set? %)) - "Anything that turns into a json array"

dominicm19:04:48

Looks like data.json uses (instance? java.util.Collection %)

qqq19:04:05

maybe all I want is vector? and list? -- are there any other rodered things besodes lists and vetors?

noisesmith19:04:19

@domingues strings and hash-maps are seqable? though, I think he wants sequential

noisesmith19:04:31

@qqq yes, arrays for example

zakwilson19:04:33

Strings are ordered.

qqq19:04:54

you knowwhat, #(or (list? %) (vector? %)) is poerfecly fine:-)

noisesmith19:04:05

@qqq fyi lazy-seqs return false for list?

rauh19:04:05

@qqq list? is almost never what you want

qqq19:04:35

list? doesn't handle lazy lists ?

noisesmith19:04:59

you'll find clojure itself never uses the term "lazy list" anywhere

noisesmith19:04:05

they are lazy-seqs

qqq19:04:19

so #(or (seq? %) (vector? %)) ?

noisesmith19:04:14

so why does sequential? not work for you here?

noisesmith19:04:16

just curious

luxbock19:04:32

is it a bad idea to define tests "inline" in the same namespace as the code being tested?

qqq19:04:46

@noisesmith : it might work; I've just never used sequential? before

noisesmith19:04:25

oh, that's funny because ArrayList prints as [] and HashMap prints as {}

qqq19:04:35

yeah, that confused me

qqq19:04:52

how do I trigger clojurebot?

noisesmith19:04:00

you use /clj

qqq19:04:59

alright, I'm using sequential?

qqq19:04:04

thanks for (1) sequential? and (2) /clj

Benjamin C19:04:50

@luxbock you don’t want to include tests inline. This would mean that the tests would be shipped with your packaged code. And additionally, when you write tests you want to be testing the external API, not have access to internal state or private functions of a namespace

luxbock19:04:49

@benjamingramlich yeah that makes sense, but I woud like to include tests as documentation, so my next question is if there's an easy way to do this without suffering from the issue you mentioned

rauh19:04:46

You can set *load-tests*

luxbock19:04:23

I can also define tests in the meta-data of the function

luxbock19:04:35

so perhaps that's the idiomatic way to do this

noisesmith19:04:48

it's a feature that I've never seen anyone use

noisesmith19:04:07

the problem is that if you define the tests via metadata, that means if someone else uses clojure.test they run your tests

noisesmith19:04:15

since they can't load your code without loading your tests

luxbock19:04:40

hmm yeah so I guess libraries should not do that

luxbock19:04:35

with spec it might be best to just include this in the :fn part of the fdef

luxbock19:04:39

what is a good naming convention for functions that create lookup maps? if the map is a -> b, I would use a-to-b-map

noisesmith19:04:57

I often just name it a->b

bcbradley19:04:43

i generally name it a?

luxbock19:04:07

yeah I like that especially when a or b can contain dashes in their names, but it bothers me a little bit that this would be a lot more accurate name for the return value of the function, so I often use that as a local variable name

bcbradley19:04:00

wait when you say functions that create lookup maps do you mean a function that is defined as a map or a function that returns an associative like a map

bcbradley19:04:39

oh, well in that case i wouldn't worry so much about the name

bcbradley19:04:56

just pick what feels right

luxbock19:04:52

yeah the reason I asked is because I find myself liking different conventions at different times, and it feels like I should just choose one and stick to it

bcbradley19:04:08

this is tanatamount to where to put {

bcbradley19:04:24

your time is better spent thinking of real problems imho 🙂

luxbock19:04:01

perhaps the larger issue is that I have a bad habit of fixing styling stuff while I procrastinate on a difficult problem

bcbradley19:04:19

if it is just a function name then you can always rename it later

bcbradley19:04:24

use search and replace

mobileink20:04:52

@luxbock all maps are lookup maps.

qqq21:04:32

I've solved this before but never liked my solution. What is the best way to do a "stateful map" in clojure? Regular map is: (a -> b) -> list of a -> list of b Stateful map is: (s -> a -> [s, b]) -> s -> list of a -> [s, list of b]

bcbradley21:04:04

that syntax is difficult to understand, atleast for me personally

bcbradley21:04:08

try explaining it

qqq21:04:33

@bcbradley : is that in response to @luxbock or my question ?

qqq21:04:54

the -> is just haskell type notation

qqq21:04:10

(a -> b) -> list a -> list b means given a function f :: a -> b and a list of a we generate a list of b

qqq21:04:39

in stateful mpa, we change f from f :: a -> b to f :: s -> a -> [s, b] so our f now (1) takes a state, (2) takes an 'a', and (3) returns a new state + a 'b'

qqq21:04:46

then we want to thread the new state through, and get a list of the b's

bcbradley21:04:39

i could be misinterpreting you but i figure that assoc seems to give what you want

bcbradley21:04:05

let the map hold all state, if you want to "add" a new state, then generate a new map with that state added by using assoc

qqq21:04:07

probably not, the solution I have at the moment is:

(defn smap [f s lst]
  (reduce
    (fn [[old accum] x]
      (let [[new v] (f x)]
       [new (conj accum v)]))
  [s []]
  lst))

bcbradley21:04:13

ok hold on a second now i understand you

bcbradley21:04:28

it doesn't help that map has two meanings in our parlance

bcbradley21:04:41

what you are describing is a classic reduce, and it looks like that is what you are using

noisesmith21:04:57

also looks like a monad as designed by Rube Goldberg

bcbradley21:04:46

honestly i find that my clojure code becomes magnitudes simpler if i just be honest with myself about where state is and how it got there

bcbradley21:04:09

its usually a bad idea to use the "the whole world -> the whole new world" pattern at a high level in almost every language i can think of, but because of clojure's design I can honestly say that it actually produces some fairly clean clojure code and I haven't had any issues yet

qqq21:04:26

here, 's' is a very small state

qqq21:04:32

think unification over a sequence of varaibles

qqq21:04:42

you want to "thread the new state" over each pair of exprs to unify

bcbradley21:04:33

so you are going from one function to another, and they both happen to be maps

bcbradley21:04:44

maps can be interpreted as reduces

bcbradley21:04:55

a reduction to a reduction sounds a lot like a transducer

qqq21:04:09

it's hard to explain

qqq21:04:17

look at the unification part of https://github.com/halgari/odin

bcbradley21:04:46

i still stand by what i said, there is no reason you can't use a transducer in a macro

qqq21:04:53

I think this is the second time you have, without understanding the problem I'm working on, just assume that whatever idea you have in mind works better. I don't quite appreciate that, and will leave the conversation at this. 🙂

lvh21:04:06

@tbaldridge Do you know if anyone’s compared odin to datascript for perf? It seems like they have comparable querying. Currently, I have nested maps that I’m turning into flattened facts so that datascript can query it (a la intension, if you’ve seen that). odin claims it’ll probably be faster than set-based approaches if you can use its laziness, but slower if you need all the results. I need all the results, but turning that map into a db isn’t free either 🙂

qqq22:04:36

@lvh: I have recently been writing something similar to a mini- {datascript/odin}

qqq22:04:50

in my experience, querying is fine, it's indexing that's far far more expensive

qqq22:04:02

so with flat maps, suppose you're adding a new entity with 10 items, it's a single assoc!

qqq22:04:22

with any form of indexing, if you're now touching an index for every kv paif of the entity; then there's stuff for foreign keys, unique key, etc ...

lvh22:04:46

right now the dbs aren’t indexed at all, I’m querying [[:deeply :nested :flat :fact 0 "value"] ...]

lvh22:04:01

so it’s, uh, cheap-ish

lvh22:04:43

but I feel like it should know more about the structure it’s querying because it’s a pretty deep tree

lvh22:04:28

If I could get cheap unification out of specter I’d be pretty happy; maybe the real answer is going to be something along the lines of: use specter to select possibly-relevant facts instead of eagerly selecting all of them, and then use datascript once the set is small enough i don’t care about the size

qqq22:04:52

@lvh: does it sound like I'm misunderstanding your question? (in my experience, datascript auto idnexs everything; and I don't understand why you have deeply nested maps in a database)

lvh22:04:52

(for context: (count that-set-of-facts) is about 1E6)

lvh22:04:31

@qqq datascript will also query seqs of facts for you that don’t live in a db, don’t have BTSet indices, &c

qqq22:04:55

what is "cheap unification"? unification is about 100-200 loc to implement; [ in fact, I'm rewriting one right now ]; if certain unification algos can speed up your dataset, it shouldnt' be hard to hack

lvh22:04:46

@qqq sure; writing unifiers isn’t hard, and there are plenty of off-the-shelf ones I can use; it’s more the query language I’m worried about

lvh22:04:19

I want humans to write queries that look like datalog, ideally without having to explode that nested map into a db or seq-of-facts 🙂

lvh22:04:11

that’s pretty easy to do when it’s just a simple select — '["xyz" _ _ 0 _] is easy enough to translate to a specter navigator

qqq22:04:48

dpes spectre navigator linear search all facts?

lvh22:04:14

I mean, mine does

lvh22:04:22

it’s pretty good at getting a subset of the tree

qqq22:04:31

are all your tuples of the same length?

qqq22:04:50

can you run group-by on your dataset before hand, and just use that for indexing?

qqq22:04:57

(assuming all your data is tuples right?)

lvh22:04:12

I do do something like that (nested group-by) but that’s more because then diffs are better

qqq22:04:42

it just seems like selecting all items (via cached group-y) where first elemtn is "xyz" is better than linear search

lvh22:04:49

honestly the perf of the unindexed version is mostly fine, especially if I can optimize so that it only dumps the facts it should actually care about for queries

qqq22:04:13

we have exchanged many facts, which we agree on; I forgot the question we're debating 🙂

lvh22:04:18

so what I would like is, datascript except I can just give it a nested data structure

lvh22:04:41

which I think odin … sorta is? obviously totally different syntax

qqq22:04:01

I can't speak for odin; I watched some of Tim's videos, but I never used Odin.

qqq22:04:16

I can say, for a similar problem, of searching "nested data", I did the following:

qqq22:04:41

(1) took the nested data, flattened it ao a list of tuples (2) ran group-by on it many times, indexing it in all the ways I cared about (3) that sufficed for my needs

bcbradley22:04:01

would something like this work @qqq

(defn statefully-map [f]
  (fn [s]
    [s (f s)]))
(defn b-seq [initial-state f]
  (let [f (statefully-map f)]
    (map second (reduce (comp f first) [initial-state nil] a-seq))))

bcbradley22:04:31

well i guess a-seq should be an argument but you get the idea

lvh22:04:59

statefully map reads like… (juxt identity f) to me

lvh22:04:21

(not a criticism, just an observation)

bcbradley22:04:22

yeah that would work too

bcbradley22:04:44

i started out trying to implement the "aggregating" behavior using transducer reduction state (volatile!)

bcbradley22:04:04

thats why statefully-map looks kind of like a transducer (minus the other two arities)

bcbradley22:04:22

i'm not comfortable enough with actually implementing transducers to do that

bcbradley22:04:37

but the general idea would be to make a new transducible process that "peels out" the b-seq

bcbradley22:04:55

ie, not (transduce) or (educe) something you make up

qqq22:04:22

I can't off the top of my head point out where we disagree, but I don't thikn these two things are the same.

bcbradley22:04:47

well darn i'm sorry if i've wasted your time

qqq22:04:48

(defn smap [f s lst]
  (reduce
    (fn [[old accum] x]
      (let [[new v] (f x)]
       [new (conj accum v)]))
  [s []]
  lst))
is definitely not equiv to (juxt identity f)

qqq22:04:16

nothing to apologize for; I guess I expalined this poorly

qqq22:04:38

basically, I wanted a very very poor man's version of https://wiki.haskell.org/State_Monad

bcbradley22:04:54

i just took your haskel definition of f:: a->b and f:: s->a->b and turned it into statefully-map

qqq22:04:10

it's not f :: s -> a -> b, it's f :: s -> a -> [s, b]

qqq22:04:19

f takes old state + 'a' and returns new state + b

bcbradley22:04:20

yeah thats what i meant

bcbradley22:04:29

the function satisfies that definition

bcbradley22:04:39

but maybe i'm interpreting you too literally

bcbradley22:04:48

i interpreted [s, b] as literally a vector

qqq22:04:59

yes, it's a literal vector

qqq22:04:06

okay, maybe I misread your solution; I'm being an idiot

qqq22:04:11

I should just run your code and see if we get the same answer

bcbradley22:04:40

no wait now that i think about it you might be right

bcbradley22:04:52

wow its hard to talk about this stuff lol

qqq22:04:03

(defn statefully-map [f]
  (fn [s]
    [s (f s)]))
(defn b-seq [initial-state f]
  (let [f (statefully-map f)]
    (map second (reduce (comp f first) [initial-state nil] a-seq))))
^^ in your above code, where is a-seq defined ?

bcbradley22:04:17

i meant to pull it into the argument list

bcbradley22:04:10

the part that confused me about your haskel definition was a of the f :: s -> a -> [s, b]

bcbradley22:04:44

i figure a is going to b with the definition provided earlier right?

bcbradley22:04:19

so that means given the original function f, b is (f s) or is it (f x) with some new variable x?

bcbradley22:04:22

thats where i was iffy

bcbradley22:04:44

i chose (f s), and you can see it in statefully-map

qqq22:04:42

err, a function f would look something like:

(defn f [s a]
  [(+ s 1) (+ s a)])
where (+ s 1) is the "new state" and (+ s a) is the "b"

bcbradley22:04:04

oh well then i was way off

qqq22:04:39

I guess I should ahve riten f :: [s a] -> [s b] instead of f :: s -> a -> [s b]

qqq22:04:50

I think that cause you to write the extra level of fn []

bcbradley22:04:35

so what is + here?

bcbradley22:04:42

is it just addition?

bcbradley22:04:19

or is this pseudocode

bcbradley22:04:45

i mean its confusing when you said earlier that f:: a -> b and yet b clearly depends on s in the derived definition

qqq22:04:24

in "standard map, we have f :: a -> b" in "stateful map, we have f :: [s, a] -> [s, b] "

qqq22:04:41

where the two s's can be different

bcbradley22:04:49

ah, ok thats the important bit

bcbradley22:04:01

so what function is responsible for mapping the s to the s`

bcbradley22:04:28

so f maps both a->b and s->s`?

qqq22:04:37

(defn [s a]
  [(+ s 1) (+ s a)])

qqq22:04:58

in this case f says: new s = (+ s 1) b = (+ s a)

bcbradley22:04:03

what if you defined f in terms of g and h

bcbradley22:04:15

and g did the left part, and h did the right part

bcbradley22:04:17

that way you can keep working with simple terms rather than complex terms

bcbradley22:04:46

like, build up a vector of all the g's and one of all the h's, then "zip them" into the pairs that you want

qqq22:04:45

in theory, it could work, I'm just stealing notation from Haskell's state monad, which does things like: s -> a -> (s, b)

tbaldridge23:04:02

@qqq Well if you really want the state monad that sort of thing does exist in Clojure. But often with a few atoms you can accomplish the same thing, while still being able to use clojure.core functions

qqq23:04:36

@tbaldridge : I have a weird aversion to using atoms for this purpose. I can't justify my dislike.